package bg.consulting.labo;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.media.CaptureDeviceInfo;
import javax.media.CaptureDeviceManager;
import javax.media.ControllerErrorEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.DataSink;
import javax.media.Format;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.Processor;
import javax.media.ProcessorModel;
import javax.media.RealizeCompleteEvent;
import javax.media.StopEvent;
import javax.media.datasink.DataSinkErrorEvent;
import javax.media.datasink.DataSinkEvent;
import javax.media.datasink.DataSinkListener;
import javax.media.datasink.EndOfStreamEvent;
import javax.media.format.AudioFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
import javax.media.protocol.FileTypeDescriptor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

import bg.consulting.labo.util.UtilJMF;
import bg.consulting.labo.util.UtilSwing;


public class Recorder implements ControllerListener {

	private JPanel panelGlobal = new JPanel();
	/** Processor used to transcode the media. */
	protected Processor processor;
	protected File fileDestination = new File("capturedSound3.wav");
	/** Sink used to "write" out the transcoded media. */
	protected DataSink dataSink;
	
	private List<IDataSourceProvider> listDataSourceProvider = new ArrayList<IDataSourceProvider>();
	/**
	 * Model used in constructing the processor, and which specifies track
	 * formats and output content type
	 */
	protected ProcessorModel processorModel;

	/** Output of the Processor: the transcoded media. */
	protected DataSource dataSource;

	
	DataSource dataSourceMerger ;
	
	public JPanel getPanelGlobal() {
		return panelGlobal;
	}

	public static void main(String[] a) throws Exception{
		Recorder record = new Recorder();
		JFrame frame = new JFrame();
		frame.add(record.getPanelGlobal());
		UtilSwing.initFrame(frame);
		Microphone micro = new Microphone(); 
		record.addDataSourceProvider(micro);
	}

	public Recorder() {
		initFile();
		initSwing();
	}
	
	private void initFile() {
		try {
			if (!fileDestination.exists()){
				this.fileDestination.delete();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void initSwing() {
		
		
		JButton buttonRecordInit= new JButton("Init");
		buttonRecordInit.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				initRecord();
			}
		});
		JButton buttonRecordStop = new JButton("stop");
		buttonRecordStop.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				stopRecord();
			}
		});
		JButton buttonRecord = new JButton("record");
		buttonRecord.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				startRecord();
			}
		});
		
			
		panelGlobal.add(buttonRecordInit);
		panelGlobal.add(buttonRecord);
		panelGlobal.add(buttonRecordStop);
	}

	
   DataSource[] getDataSources() {
	   List<DataSource> listDataSources = new ArrayList<DataSource>();
	   for(IDataSourceProvider idp : listDataSourceProvider){
		   DataSource ds = idp.getDataSource();
		   if (ds == null){
		   }else {
			   System.out.println("Recorder.getDataSources format: "+UtilJMF.getFormat(ds));
			   listDataSources.add(ds);
		   }
	   }	   
	   return listDataSources.toArray(new DataSource[0]);
   }
	
	private  void initDataSourceMerger() {
		// Merge the data sources from the individual processors
		try {
			DataSource[]  dataSources = this.getDataSources();
			System.out.println("Nb de sources : "+dataSources.length);
			this.dataSourceMerger = Manager.createMergingDataSource(dataSources);
			this.dataSourceMerger.connect();
			this.dataSourceMerger.start();			
		} catch (Exception ex) {
			System.err.println("doMerge " + ex);			
		}
	}

	private DataSource getDataSourceFirst() {
		return this.listDataSourceProvider.get(0).getDataSource();
	}
	

	private void initRecord() {
		try {
			System.out.println("startRecord " );
			Format[] formatsArray = {new AudioFormat(AudioFormat.MPEGLAYER3)};
			//Format[] formatsArray = {new AudioFormat(AudioFormat.MPEGLAYER3),new AudioFormat(AudioFormat.LINEAR)};
			ContentDescriptor contentDescriptor = new ContentDescriptor(FileTypeDescriptor.WAVE);
			//ContentDescriptor contentDescriptor = new ContentDescriptor(FileTypeDescriptor.MPEG_AUDIO);
			
			initDataSourceMerger();
			processorModel = new ProcessorModel(this.dataSourceMerger, formatsArray, contentDescriptor);
			System.out.println("AAAAAAAAAAAAAAAAAAAAAAAAAAa");
			Thread.sleep(5000);

			processor = Manager.createRealizedProcessor(processorModel);
			

			UtilJMF.waitUntilRealize(processor);
			processor.addControllerListener(this);
			System.out.println(" processor state : "+processor.getState()+"   "+UtilJMF.getProcessorState(this.processor));
			Thread.sleep(3000);
			System.out.println(" processor state : "+processor.getState());
			float rate = 1.0f;			
			float translationRate = processor.setRate((float) Math.abs(rate));
			this.dataSource = processor.getDataOutput();
			
			MediaLocator mediaLocatorDestination = new MediaLocator(""+this.fileDestination.toURI());
			
			this.dataSink = Manager.createDataSink(dataSource, mediaLocatorDestination);
			dataSink.addDataSinkListener(new DataSinkListener() {
				public void dataSinkUpdate(DataSinkEvent e) {
					System.out.println("dataSinkUpdate DataSinkEvent e :");
					if (e instanceof EndOfStreamEvent) {
						dataSink.close();
						dataSource.disconnect();
					} else if (e instanceof DataSinkErrorEvent) {
						if (dataSink != null)
							dataSink.close();
						if (dataSource != null)
							dataSource.disconnect();
					}
				}
			});
			System.out.println("startRecord done : file exists: AAAA "+fileDestination.exists()+"  "+fileDestination.getAbsolutePath() );
			processor.start();
			System.out.println("startRecord done : file exists: BBBBB "+fileDestination.exists()+"  "+fileDestination.getAbsolutePath() );
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private void startRecord() {
		System.out.println("StartRecord fileDestination.exists :"+this.fileDestination.exists()+"  size: "+this.fileDestination.length());
		System.out.println("StartRecord dataSink.getOutputLocator :"+this.dataSink.getOutputLocator());
		try {
			this.dataSink.open();
			this.dataSink.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void stopRecord() {
		System.out.println("StopRecord fileDestination.exists :"+this.fileDestination.exists()+"  size: "+this.fileDestination.length());
		try {
			//this.dataSink.stop();
			//this.dataSink.close();
			this.processor.stop();
	       
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("StopRecord fileDestination.exists :"+this.fileDestination.exists()+"  size: "+this.fileDestination.length());
		
	}

	public static List getListAudioCaptureDeviceName() {
		Vector v = CaptureDeviceManager.getDeviceList(null);
		List<String> listAudioCAptureDevice = new ArrayList<String>();
		for (Object o : v) {
			CaptureDeviceInfo cdi = (CaptureDeviceInfo) o;
			boolean isAudio = cdi.getFormats()[0] instanceof AudioFormat;
			System.out.println("    " + cdi.getName() + "  isAudio :" + isAudio);
			if (isAudio) {
				listAudioCAptureDevice.add(cdi.getName());
			}
		}
		System.out.println("getListAudioCaptureDevice: size:" + listAudioCAptureDevice.size());
		return listAudioCAptureDevice;
	}

	@Override
	public void controllerUpdate(ControllerEvent event) {
		System.out.println("controllerUpdate ControllerEvent "+UtilJMF.getProcessorState(this.processor)+" event : " + event);
		if (event instanceof StopEvent) {
			System.out.println("controllerUpdate ControllerEvent StopEvent !!!!!!!!!!!!!!");
			processor.close();
			// if (state == TRANSLATING) state = TRANSFERRING;
		}
		// Transcoding failed.
		else if (event instanceof ControllerErrorEvent) {
			System.out.println("controllerUpdate ControllerEvent ControllerErrorEvent !!!!!!!!!!!!!");
			processor.removeControllerListener(this);
			processor.close();
			// state = FAILED;
		}else if (event instanceof RealizeCompleteEvent){
			System.out.println("controllerUpdate ControllerEvent Yes Realize Termined !!!!!!!!!!!!!");
		}
	}
	
	public void addDataSourceProvider(IDataSourceProvider dataSourceProvider){
		this.listDataSourceProvider.add(dataSourceProvider);
	}
	
	
}
